<?php

namespace app\dev\controller;

use think\Db;


/**
 * 升级控制器
 */
class Update extends \think\Controller{

    protected $log = false;

    const UPDATE_PWD = '1284';

    protected $start_time = null;

    protected $update_file_dir = null;

    protected $pidFile = null;

    public function __construct()
    {
        parent::__construct();

        ini_set('max_execution_time',0);
        
        $this->start_time = microtime(true);
        
        $this->pwd = $this->request->param('pwd');
        if($this->pwd != self::UPDATE_PWD){
            throw new \Exception("Incorrect Pwd For Update:{$this->pwd}");
        }
        $this->version = $this->request->param('version',$this->request->param('v'));
        if(empty($this->version)){
            throw new \Exception("Incorrect Version For Update");
        }
        $this->version = "V".strtoupper(str_replace(".","_",$this->version));
        $this->update_file_dir = dirname(__FILE__)."/../version/".$this->version;
        if(!is_dir($this->update_file_dir)){
            throw new \Exception("FileDir not exist:{$this->update_file_dir}");
        }

        set_error_handler([$this,'errorHandle']);
        set_exception_handler([$this,'exceptionHandle']);

        if($this->request->param('log')!=0){
            $logFileName = date('YmdHis');
            $this->log = fopen($this->update_file_dir."/update_{$logFileName}.log",'a+');
        }
        
        

        $this->pidFile = $this->update_file_dir."/pid";

        $this->processCheck();
    }

    /**
     * 执行更新程序
     */
    public function exec(){
        $desc = $this->update_file_dir.("/description");
        if(is_file($desc)){
            $this->execLog(file_get_contents($desc));
        }
        $this->execLog("...正在执行更新程序");

        // 数据库更新
        $this->updateDb();

        // 执行更新程序
        $className = "\\app\\dev\\version\\{$this->version}\\updateExec";
        if( class_exists($className) ){
            $object = new $className($this);
            // var_dump($object);
            if(method_exists($object, "doExec"))
            {
                $this->execLog("检测到更新执行文件可执行，准备执行更新指令");
                call_user_func_array([$object, "doExec"], []);
            }
            else{
                $this->execLog("检测到更新执行文件不可执行");
            }
        }else{
            $this->execLog("未检测到可更新执行文件");
        }
        $this->execLog("更新完成!");
    }


    /**
     * 数据库更新
     */
    public function updateDb(){
        $sql = $this->update_file_dir."/update.sql";
        if(!is_file($sql)){
            return $this->execLog("...没有数据库结构更新");
        }
        $this->execLog("...开始读取数据库更新");

        $sql = file_get_contents($sql);

        if($sql)
        $this->execLog("...开始更新数据库");
        else 
        $this->execLog("数据库更新文件读取为空，暂无更新!!!!!");

        Db::startTrans();
        $sqls = explode(';',$sql);
        try {
            foreach ($sqls as $key => $value) {
                if( trim($value) && strpos($value, '--') === false ){
                    $this->execLog("SQL{$key}: {$value}");
                    $sqlres = Db::execute($value);
                    $this->execLog("__SQLRES: {$sqlres} ");
                }
            }
        } catch (\Exception $th) {
            Db::rollback();
            $this->execLog("Error: 数据库更新失败!!!");
            throw $th;
        }
        Db::commit();
        $this->execLog("Success：数据库更新完毕");
    }



    /**
     * 执行记录
     */
    public function execLog($string = ""){
        $string .= "\n";
        echo $string;
        if($this->log){
            fwrite($this->log, $string);
        }
    }

    public function __destruct()
    {
        $usetime = microtime(true) - $this->start_time;
        $this->execLog("用时：{$usetime}s");
        if($this->log){
            fclose($this->log);
            $this->log = null;
        }
        if(is_file($this->pidFile)){
            unlink($this->pidFile);
        }
    }

    protected function processCheck(){
        if(is_file($this->pidFile)){
            throw new \Exception("已经有更新进程在运行，此次将删除该文件，请谨慎运行更新程序：{$this->pidFile}");
        }
        touch($this->pidFile);
    }

    function errorHandle($errorno,$errstr ,$errfile, $errline){
        return false;
        $this->execLog("Eerror_detail:");
        $this->execLog("__errorno:{$errorno}");
        $this->execLog("__errstr:{$errstr}");
        $this->execLog("__errfile:{$errfile}");
        $this->execLog("__errline:{$errline}");
         
    }

    function exceptionHandle($e){
        $this->execLog("Exception_detail:");
        $this->execLog("__msg:{$e->getMessage()}");
        $this->execLog("__file:{$e->getFile()}");
        $this->execLog("__line:{$e->getLine()}");
        $this->execLog("__trace:");
        foreach ($e->getTrace() as $key => $value) {
            $this->execLog("____{$key}");
            if(isset($value['file']))
            $this->execLog("______FILE:{$value['file']}");
            if(isset($value['line']))
            $this->execLog("______LINE:{$value['line']}");
            if(isset($value['function']))
            $this->execLog("______FUNC:{$value['function']}");
            // $this->execLog("______ARGS:".json_encode($value['args']));
        }
        unset($e, $key,$value);
    }
}

